Skip to content

Features/refine template config#1321

Draft
iceljc wants to merge 8 commits intoSciSharp:masterfrom
iceljc:features/refine-template-config
Draft

Features/refine template config#1321
iceljc wants to merge 8 commits intoSciSharp:masterfrom
iceljc:features/refine-template-config

Conversation

@iceljc
Copy link
Copy Markdown
Collaborator

@iceljc iceljc commented Apr 10, 2026

No description provided.

@iceljc iceljc marked this pull request as draft April 10, 2026 21:38
@qodo-code-review
Copy link
Copy Markdown
Contributor

Review Summary by Qodo

Add template LLM configuration support with inheritance

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add template LLM configuration support with response format settings
• Implement agent template detail retrieval with LLM config inheritance
• Use template-specific LLM config in instruct mode execution
• Refactor template config loading and persistence in file repository
Diagram
flowchart LR
  A["Agent Template"] -->|"inherits from"| B["AgentTemplateConfig"]
  B -->|"contains"| C["LlmConfig"]
  C -->|"extends"| D["LlmConfigBase"]
  E["InstructService"] -->|"retrieves"| F["Template Detail"]
  F -->|"uses LLM config"| G["Chat Completion"]
  H["FileRepository"] -->|"loads/saves"| I["template_configs.json"]
  I -->|"provides"| C
Loading

Grey Divider

File Changes

1. src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs ✨ Enhancement +2/-1

Add GetAgentTemplateDetail method signature

src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs


2. src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentTemplate.cs ✨ Enhancement +22/-3

Refactor AgentTemplate with config inheritance structure

src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentTemplate.cs


3. src/Infrastructure/BotSharp.Abstraction/Models/LlmConfigBase.cs ✨ Enhancement +8/-1

Add JSON serialization attributes and validation property

src/Infrastructure/BotSharp.Abstraction/Models/LlmConfigBase.cs


View more (14)
4. src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs ✨ Enhancement +2/-0

Add GetAgentTemplateDetail repository method

src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs


5. src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs Formatting +35/-4

Modernize collection initialization syntax

src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs


6. src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs ✨ Enhancement +30/-0

Implement GetAgentTemplateDetail with LLM config fallback

src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs


7. src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs ✨ Enhancement +26/-6

Apply template LLM config in instruction execution

src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs


8. src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Instruct.cs ✨ Enhancement +15/-3

Use template LLM config when building inner agent

src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Instruct.cs


9. src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs ✨ Enhancement +82/-2

Persist and retrieve template configs from JSON file

src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs


10. src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs ✨ Enhancement +40/-1

Add template config file handling and loading logic

src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs


11. src/Infrastructure/BotSharp.OpenAPI/Controllers/Agent/AgentController.cs Formatting +1/-1

Modernize collection initialization syntax

src/Infrastructure/BotSharp.OpenAPI/Controllers/Agent/AgentController.cs


12. src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentTemplateLlmConfigMongoModel.cs ✨ Enhancement +43/-0

Create MongoDB model for template LLM configuration

src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentTemplateLlmConfigMongoModel.cs


13. src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentTemplateMongoElement.cs ✨ Enhancement +9/-3

Add response format and LLM config to MongoDB template model

src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentTemplateMongoElement.cs


14. src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs ✨ Enhancement +18/-0

Implement GetAgentTemplateDetail for MongoDB repository

src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs


15. tests/BotSharp.LLM.Tests/Core/TestAgentService.cs 🧪 Tests +5/-0

Add GetAgentTemplateDetail mock implementation

tests/BotSharp.LLM.Tests/Core/TestAgentService.cs


16. src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj ⚙️ Configuration changes +4/-0

Add template_configs.json to project resources

src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj


17. src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/template_configs.json ⚙️ Configuration changes +1/-0

Create empty template configs file for agent

src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/template_configs.json


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented Apr 10, 2026

Code Review by Qodo

🐞 Bugs (5) 📘 Rule violations (4) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Template override drops settings 🐞 Bug ≡ Correctness ⭐ New
Description
When a template has a valid LlmConfig, InstructService replaces agent.LlmConfig with a new
AgentLlmConfig that only copies Provider/Model/MaxOutputTokens/ReasoningEffortLevel, resetting other
AgentLlmConfig fields to defaults for that run. This can change behavior (e.g., recursion depth or
modality configs) whenever template overrides are used.
Code

src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs[R245-261]

+        var prompt = string.Empty;
+        var llmConfig = agent.LlmConfig;
+
+        if (!string.IsNullOrEmpty(templateName))
+        {
+            prompt = agentService.RenderTemplate(agent, templateName);
+            var templateLlmConfig = agent.Templates?.FirstOrDefault(x => x.Name.IsEqualTo(templateName))?.LlmConfig;
+            if (templateLlmConfig?.IsValid == true)
+            {
+                llmConfig = new AgentLlmConfig
+                {
+                    Provider = templateLlmConfig.Provider,
+                    Model = templateLlmConfig.Model,
+                    MaxOutputTokens = templateLlmConfig.MaxOutputTokens,
+                    ReasoningEffortLevel = templateLlmConfig.ReasoningEffortLevel
+                };
+            }
Evidence
RunLlm constructs a brand-new AgentLlmConfig from the template and passes it into
CompletionProvider/GetChatCompletion, so any fields not explicitly copied are lost. AgentLlmConfig
defines multiple additional settings beyond the four fields being copied, so template overrides will
implicitly reset them.

src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs[244-270]
src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Instruct.cs[56-91]
src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentLlmConfig.cs[3-65]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
When template LLM overrides apply, the code constructs a new `AgentLlmConfig` with only a subset of properties, which resets other agent-level settings (e.g. recursion depth and other modality configs).

### Issue Context
Template LLM config should override only the fields it intends to change while preserving the rest of the agent’s effective LLM configuration.

### Fix
- In both places where a template-level LLM config is applied, start from the existing agent config (clone/copy) and override only the specific fields from the template.
- Ensure fields like `IsInherit`, `MaxRecursionDepth`, and any nested configs remain unchanged unless explicitly overridden.

### Fix Focus Areas
- src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs[244-270]
- src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Instruct.cs[56-91]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. No guard for splitIdx 📘 Rule violation ☼ Reliability
Description
GetAgentTemplateDetail uses LastIndexOf(".") and then calls Substring without guarding for
-1, which can throw at the storage boundary when files don't match the expected naming format.
This violates the requirement to add explicit null/empty/invalid guards at provider boundaries and
return safe fallbacks.
Code

src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs[R746-750]

+                var fileName = file.Split(Path.DirectorySeparatorChar).Last();
+                var splitIdx = fileName.LastIndexOf(".");
+                var name = fileName.Substring(0, splitIdx);
+                var extension = fileName.Substring(splitIdx + 1);
+                if (name.IsEqualTo(templateName) && extension.IsEqualTo(_agentSettings.TemplateFormat))
Evidence
PR Compliance ID 2 requires null/empty/invalid-input guards at storage/provider boundaries to avoid
runtime exceptions and return safe fallbacks. The new filename parsing can throw
ArgumentOutOfRangeException when splitIdx is -1 (or otherwise invalid) due to missing
validation.

src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs[746-750]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Filename parsing assumes every file name contains a `.` and uses `Substring` with an unchecked index, which can throw and break reads at the repository boundary.
## Issue Context
This code runs against files in a directory and must tolerate unexpected/invalid file names.
## Fix Focus Areas
- src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs[746-750]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Case-sensitive TemplateName match 📘 Rule violation ≡ Correctness
Description
Template selection compares identifier-like names with ==, which is case-sensitive and can behave
inconsistently for identifier matching. This violates the requirement to use ordinal,
case-insensitive comparisons for identifiers.
Code

src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Instruct.cs[69]

+                var template = agent?.Templates?.FirstOrDefault(x => x.Name == options.TemplateName);
Evidence
PR Compliance ID 4 requires StringComparison.OrdinalIgnoreCase (or equivalent) for identifier-like
string comparisons. The new code uses x.Name == options.TemplateName instead of an ordinal,
case-insensitive comparison (e.g., IsEqualTo or Equals(..., OrdinalIgnoreCase)).

src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Instruct.cs[69-69]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Template lookup uses a case-sensitive equality operator for an identifier-like string (`TemplateName`), risking mismatches.
## Issue Context
Identifier comparisons must be culture-invariant and case-insensitive.
## Fix Focus Areas
- src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Instruct.cs[69-69]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (2)
4. Mongo patch drops config 🐞 Bug ≡ Correctness
Description
MongoRepository.PatchAgentTemplate updates only Content, so template ResponseFormat/LlmConfig
changes sent via /agent/{agentId}/templates are silently lost when Mongo storage is used.
Code

src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs[R574-576]

   public async Task<bool> PatchAgentTemplate(string agentId, AgentTemplate template)
   {
       if (string.IsNullOrEmpty(agentId) || template == null)
Evidence
The OpenAPI patch endpoint forwards AgentTemplate objects (which now include
ResponseFormat/LlmConfig) into AgentService.PatchAgentTemplate, which calls
IBotSharpRepository.PatchAgentTemplate; the Mongo implementation only assigns Content and persists
Templates, so the new config fields are never written. The Mongo template document model was updated
to include these fields, making the omission concrete data loss for Mongo-backed deployments.

src/Infrastructure/BotSharp.OpenAPI/Controllers/Agent/AgentController.cs[139-145]
src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs[58-93]
src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentTemplateMongoElement.cs[6-33]
src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs[574-597]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
MongoRepository.PatchAgentTemplate persists only `Content`, dropping new per-template fields (`ResponseFormat`, `LlmConfig`) when templates are patched.
### Issue Context
`/agent/{agentId}/templates` uses `AgentTemplate` as request payload, so clients can submit `responseFormat`/`llmConfig` but Mongo storage will ignore them.
### Fix Focus Areas
- src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs[574-597]
### Implementation notes
Update the found template element to include:
- `foundTemplate.ResponseFormat = template.ResponseFormat;`
- `foundTemplate.LlmConfig = AgentTemplateLlmConfigMongoModel.ToMongoModel(template.LlmConfig);`
(or replace the element in the list with `AgentTemplateMongoElement.ToMongoElement(template)`), then persist the updated templates array.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. response_format unused 🐞 Bug ≡ Correctness
Description
AgentTemplateConfig adds/persists ResponseFormat, but response-format detection still ignores it, so
setting response_format in template_configs.json has no effect on the JSON-repair path.
Code

src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentTemplate.cs[R23-37]

+public class AgentTemplateConfig
+{
+    public string Name { get; set; }
+
+    /// <summary>
+    /// Response format: json, xml, markdown, yaml, etc.
+    /// </summary>
+    [JsonPropertyName("response_format")]
+    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+    public string? ResponseFormat { get; set; }
+
+    [JsonPropertyName("llm_config")]
+    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+    public AgentTemplateLlmConfig? LlmConfig { get; set; }
+}
Evidence
Templates now load/persist ResponseFormat from template configs, but GetTemplateResponseFormat still
infers Json/Text solely from a hard-coded marker inside template content; InstructService.Execute
relies on GetTemplateResponseFormat to decide whether to run JSON repair, so ResponseFormat
configuration is effectively ignored end-to-end.

src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentTemplate.cs[23-37]
src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs[386-413]
src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs[153-162]
src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs[317-323]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`AgentTemplateConfig.ResponseFormat` is persisted/loaded but never used to determine response format; JSON repair is triggered only by a template content marker.
### Issue Context
This makes `template_configs.json`'s `response_format` ineffective, even though the model and persistence were added in this PR.
### Fix Focus Areas
- src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs[153-162]
- src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentTemplate.cs[23-37]
### Implementation notes
Update `GetTemplateResponseFormat` to:
1) Resolve the template object (not just its content).
2) If `template.ResponseFormat` is set (e.g., equals "json" case-insensitively), return `ResponseFormatType.Json`.
3) Otherwise fall back to the existing marker-based detection.
Optionally, validate/normalize allowed ResponseFormat strings to avoid surprises.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

6. Template Name may be null 🐞 Bug ☼ Reliability ⭐ New
Description
AgentTemplateConfig.Name is declared non-nullable but is not initialized, so default-constructed or
partially-deserialized configs can carry a null Name and flow into ToString()/matching logic. This
undermines the model contract and can produce unexpected null strings.
Code

src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentTemplate.cs[R23-26]

+public class AgentTemplateConfig
+{
+    public string Name { get; set; }
+
Evidence
AgentTemplateConfig.Name has no default value, while AgentTemplate.ToString() returns Name
directly. This allows runtime nulls despite the non-nullable annotation.

src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentTemplate.cs[3-37]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`AgentTemplateConfig.Name` is non-nullable but uninitialized, allowing `null` at runtime (e.g., deserialization missing the field or parameterless construction).

### Issue Context
`AgentTemplate` inherits `Name` and returns it from `ToString()`, so a null `Name` can propagate to call sites.

### Fix
- Initialize `Name`:
 - `public string Name { get; set; } = string.Empty;`
- Optionally also harden `ToString()` to `return Name ?? string.Empty;` if you want belt-and-suspenders protection.
- If you require Name for correctness, consider using `required string Name { get; set; }` (and update any deserialization/constructors accordingly).

### Fix Focus Areas
- src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentTemplate.cs[23-37]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Cached AgentTemplate returned mutable 📘 Rule violation ☼ Reliability
Description
GetAgentTemplateDetail is cached and returns a mutable AgentTemplate instance directly, so
downstream callers can mutate the cached object and leak state across requests/components. This
violates the requirement to defensively copy cached/caller-owned mutable objects before exposing
them to downstream code.
Code

src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs[R108-136]

+#if !DEBUG
+    [SharpCache(10, perInstanceCache: true)]
+#endif
+    public async Task<AgentTemplate?> GetAgentTemplateDetail(string agentId, string templateName)
+    {
+        var template = await _db.GetAgentTemplateDetail(agentId, templateName);
+        if (template == null)
+        {
+            return template;
+        }
+
+        if (template.LlmConfig == null)
+        {
+            var agent = await _db.GetAgent(agentId);
+            if (!string.IsNullOrEmpty(agent?.LlmConfig?.Provider)
+                && !string.IsNullOrEmpty(agent?.LlmConfig?.Model))
+            {
+                template.LlmConfig = new AgentTemplateLlmConfig
+                {
+                    Provider = agent.LlmConfig.Provider,
+                    Model = agent.LlmConfig.Model,
+                    MaxOutputTokens = agent.LlmConfig.MaxOutputTokens,
+                    ReasoningEffortLevel = agent.LlmConfig.ReasoningEffortLevel
+                };
+            }
+        }
+
+        return template;
+    }
Evidence
PR Compliance ID 1 requires defensive copying of cached mutable objects to prevent cross-request
leakage. The method is decorated with SharpCache and returns the cached template object without
cloning, allowing downstream mutation of a shared instance.

src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs[108-136]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`GetAgentTemplateDetail` is cached and returns a mutable `AgentTemplate` instance directly; callers can mutate it and affect subsequent cache hits.
## Issue Context
The compliance requirement calls out cached mutable objects as a key source of cross-request/component state leakage.
## Fix Focus Areas
- src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs[108-136]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. Logs full configFile path 📘 Rule violation ⛨ Security
Description
Error logs include the full configFile path, which can expose internal filesystem/configuration
details in logs. This violates the requirement to avoid exposing internal configuration values and
to minimize logged sensitive/internal data.
Code

src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs[R526-529]

+        catch (Exception ex)
+        {
+            _logger.LogError(ex, "Error when loading template configs in {configFile}", configFile);
+            return (configs, configFile);
Evidence
PR Compliance ID 3 requires minimizing sensitive/internal configuration details in logs. The new
error logs explicitly include configFile (a full internal path), which can reveal internal
repository layout and configuration locations.

src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs[526-529]
src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs[139-143]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Error logs include the full internal `configFile` path, which may expose internal configuration/location details.
## Issue Context
Compliance requires logs to avoid sensitive/internal configuration details and use minimal metadata.
## Fix Focus Areas
- src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs[526-529]
- src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs[139-143]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Advisory comments

9. LlmConfigBase JSON contract changed 🐞 Bug ⚙ Maintainability ⭐ New
Description
LlmConfigBase now pins MaxOutputTokens and ReasoningEffortLevel to snake_case JSON names, changing
the accepted/emitted JSON property names for all derived types. Any callers still sending camelCase
names will no longer bind to these fields.
Code

src/Infrastructure/BotSharp.Abstraction/Models/LlmConfigBase.cs[R5-17]

    /// <summary>
    /// Llm maximum output tokens
    /// </summary>
+    [JsonPropertyName("max_output_tokens")]
+    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public int? MaxOutputTokens { get; set; }

    /// <summary>
-    /// Llm reasoning effort level
+    /// Llm reasoning effort level, thinking level
    /// </summary>
+    [JsonPropertyName("reasoning_effort_level")]
+    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public string? ReasoningEffortLevel { get; set; }
Evidence
BotSharp’s default JSON options use a camelCase naming policy, but JsonPropertyName on these
properties forces snake_case names regardless of the policy. That changes the JSON contract for any
serialization/deserialization paths using System.Text.Json.

src/Infrastructure/BotSharp.Abstraction/Models/LlmConfigBase.cs[3-17]
src/Infrastructure/BotSharp.Abstraction/Options/BotSharpOptions.cs[5-14]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`LlmConfigBase` now requires snake_case JSON property names for `MaxOutputTokens` and `ReasoningEffortLevel`, which may break existing clients/configs that send camelCase.

### Issue Context
Default JSON options use `JsonNamingPolicy.CamelCase`, but `[JsonPropertyName]` overrides that behavior.

### Fix options
- If snake_case is intended: document the breaking change and ensure all producers are updated.
- If backward compatibility is needed: add a custom `JsonConverter<LlmConfigBase>` (or for each derived type) that accepts both `maxOutputTokens` and `max_output_tokens` (and similarly for reasoning effort).

### Fix Focus Areas
- src/Infrastructure/BotSharp.Abstraction/Models/LlmConfigBase.cs[3-17]
- src/Infrastructure/BotSharp.Abstraction/Options/BotSharpOptions.cs[5-14]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Grey Divider

Previous review results

Review updated until commit 5375da1

Results up to commit 35c44ac


Details

🐞 Bugs (2)   📘 Rule violations (4)   📎 Requirement gaps (0)   🎨 UX Issues (0)
🐞\ ≡ Correctness (2)
📘\ ≡ Correctness (1) ☼ Reliability (2) ⛨ Security (1)
Grey Divider
Action required
1. No guard for splitIdx 📘
Description
GetAgentTemplateDetail uses LastIndexOf(".") and then calls Substring without guarding for
-1, which can throw at the storage boundary when files don't match the expected naming format.
This violates the requirement to add explicit null/empty/invalid guards at provider boundaries and
return safe fallbacks.
Code

src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs[R746-750]

+                var fileName = file.Split(Path.DirectorySeparatorChar).Last();
+                var splitIdx = fileName.LastIndexOf(".");
+                var name = fileName.Substring(0, splitIdx);
+                var extension = fileName.Substring(splitIdx + 1);
+                if (name.IsEqualTo(templateName) && extension.IsEqualTo(_agentSettings.TemplateFormat))
Evidence
PR Compliance ID 2 requires null/empty/invalid-input guards at storage/provider boundaries to avoid
runtime exceptions and return safe fallbacks. The new filename parsing can throw
ArgumentOutOfRangeException when splitIdx is -1 (or otherwise invalid) due to missing
validation.

src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs[746-750]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Filename parsing assumes every file name contains a `.` and uses `Substring` with an unchecked index, which can throw and break reads at the repository boundary.

## Issue Context
This code runs against files in a directory and must tolerate unexpected/invalid file names.

## Fix Focus Areas
- src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs[746-750]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Case-sensitive TemplateName match 📘
Description
Template selection compares identifier-like names with ==, which is case-sensitive and can behave
inconsistently for identifier matching. This violates the requirement to use ordinal,
case-insensitive comparisons for identifiers.
Code

src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Instruct.cs[69]

+                var template = agent?.Templates?.FirstOrDefault(x => x.Name == options.TemplateName);
Evidence
PR Compliance ID 4 requires StringComparison.OrdinalIgnoreCase (or equivalent) for identifier-like
string comparisons. The new code uses x.Name == options.TemplateName instead of an ordinal,
case-insensitive comparison (e.g., IsEqualTo or Equals(..., OrdinalIgnoreCase)).

src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Instruct.cs[69-69]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Template lookup uses a case-sensitive equality operator for an identifier-like string (`TemplateName`), risking mismatches.

## Issue Context
Identifier comparisons must be culture-invariant and case-insensitive.

## Fix Focus Areas
- src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Instruct.cs[69-69]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Mongo patch drops config 🐞
Description
MongoRepository.PatchAgentTemplate updates only Content, so template ResponseFormat/LlmConfig
changes sent via /agent/{agentId}/templates are silently lost when Mongo storage is used.
Code

src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs[R574-576]

    public async Task<bool> PatchAgentTemplate(string agentId, AgentTemplate template)
    {
        if (string.IsNullOrEmpty(agentId) || template == null)
Evidence
The OpenAPI patch endpoint forwards AgentTemplate objects (which now include
ResponseFormat/LlmConfig) into AgentService.PatchAgentTemplate, which calls
IBotSharpRepository.PatchAgentTemplate; the Mongo implementation only assigns Content and persists
Templates, so the new config fields are never written. The Mongo template document model was updated
to include these fields, making the omission concrete data loss for Mongo-backed deployments.

src/Infrastructure/BotSharp.OpenAPI/Controllers/Agent/AgentController.cs[139-145]
src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs[58-93]
src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentTemplateMongoElement.cs[6-33]
src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs[574-597]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
MongoRepository.PatchAgentTemplate persists only `Content`, dropping new per-template fields (`ResponseFormat`, `LlmConfig`) when templates are patched.

### Issue Context
`/agent/{agentId}/templates` uses `AgentTemplate` as request payload, so clients can submit `responseFormat`/`llmConfig` but Mongo storage will ignore them.

### Fix Focus Areas
- src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs[574-597]

### Implementation notes
Update the found template element to include:
- `foundTemplate.ResponseFormat = template.ResponseFormat;`
- `foundTemplate.LlmConfig = AgentTemplateLlmConfigMongoModel.ToMongoModel(template.LlmConfig);`

(or replace the element in the list with `AgentTemplateMongoElement.ToMongoElement(template)`), then persist the updated templates array.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
4. response_format unused 🐞
Description
AgentTemplateConfig adds/persists ResponseFormat, but response-format detection still ignores it, so
setting response_format in template_configs.json has no effect on the JSON-repair path.
Code

src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentTemplate.cs[R23-37]

+public class AgentTemplateConfig
+{
+    public string Name { get; set; }
+
+    /// <summary>
+    /// Response format: json, xml, markdown, yaml, etc.
+    /// </summary>
+    [JsonPropertyName("response_format")]
+    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+    public string? ResponseFormat { get; set; }
+
+    [JsonPropertyName("llm_config")]
+    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+    public AgentTemplateLlmConfig? LlmConfig { get; set; }
+}
Evidence
Templates now load/persist ResponseFormat from template configs, but GetTemplateResponseFormat still
infers Json/Text solely from a hard-coded marker inside template content; InstructService.Execute
relies on GetTemplateResponseFormat to decide whether to run JSON repair, so ResponseFormat
configuration is effectively ignored end-to-end.

src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentTemplate.cs[23-37]
src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs[386-413]
src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs[153-162]
src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs[317-323]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`AgentTemplateConfig.ResponseFormat` is persisted/loaded but never used to determine response format; JSON repair is triggered only by a template content marker.

### Issue Context
This makes `template_configs.json`'s `response_format` ineffective, even though the model and persistence were added in this PR.

### Fix Focus Areas
- src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs[153-162]
- src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentTemplate.cs[23-37]

### Implementation notes
Update `GetTemplateResponseFormat` to:
1) Resolve the template object (not just its content).
2) If `template.ResponseFormat` is set (e.g., equals "json" case-insensitively), return `ResponseFormatType.Json`.
3) Otherwise fall back to the existing marker-based detection.

Optionally, validate/normalize allowed ResponseFormat strings to avoid surprises.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended
5. Cached AgentTemplate returned mutable 📘
Description
GetAgentTemplateDetail is cached and returns a mutable AgentTemplate instance directly, so
downstream callers can mutate the cached object and leak state across requests/components. This
violates the requirement to defensively copy cached/caller-owned mutable objects before exposing
them to downstream code.
Code

src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs[R108-136]

+#if !DEBUG
+    [SharpCache(10, perInstanceCache: true)]
+#endif
+    public async Task<AgentTemplate?> GetAgentTemplateDetail(string agentId, string templateName)
+    {
+        var template = await _db.GetAgentTemplateDetail(agentId, templateName);
+        if (template == null)
+        {
+            return template;
+        }
+
+        if (template.LlmConfig == null)
+        {
+            var agent = await _db.GetAgent(agentId);
+            if (!string.IsNullOrEmpty(agent?.LlmConfig?.Provider)
+                && !string.IsNullOrEmpty(agent?.LlmConfig?.Model))
+            {
+                template.LlmConfig = new AgentTemplateLlmConfig
+                {
+                    Provider = agent.LlmConfig.Provider,
+                    Model = agent.LlmConfig.Model,
+                    MaxOutputTokens = agent.LlmConfig.MaxOutputTokens,
+                    ReasoningEffortLevel = agent.LlmConfig.ReasoningEffortLevel
+                };
+            }
+        }
+
+        return template;
+    }
Evidence
PR Compliance ID 1 requires defensive copying of cached mutable objects to prevent cross-request
leakage. The method is decorated with SharpCache and returns the cached template object without
cloning, allowing downstream mutation of a shared instance.

src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs[108-136]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`GetAgentTemplateDetail` is cached and returns a mutable `AgentTemplate` instance directly; callers can mutate it and affect subsequent cache hits.

## Issue Context
The compliance requirement calls out cached mutable objects as a key source of cross-request/component state leakage.

## Fix Focus Areas
- src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs[108-136]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Logs full configFile path 📘
Description
Error logs include the full configFile path, which can expose internal filesystem/configuration
details in logs. This violates the requirement to avoid exposing internal configuration values and
to minimize logged sensitive/internal data.
Code

src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs[R526-529]

+        catch (Exception ex)
+        {
+            _logger.LogError(ex, "Error when loading template configs in {configFile}", configFile);
+            return (configs, configFile);
Evidence
PR Compliance ID 3 requires minimizing sensitive/internal configuration details in logs. The new
error logs explicitly include configFile (a full internal path), which can reveal internal
repository layout and configuration locations.

src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs[526-529]
src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs[139-143]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Error logs include the full internal `configFile` path, which may expose internal configuration/location details.

## Issue Context
Compliance requires logs to avoid sensitive/internal configuration details and use minimal metadata.

## Fix Focus Areas
- src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs[526-529]
- src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs[139-143]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider Grey Divider

Qodo Logo

Comment on lines +746 to +750
var fileName = file.Split(Path.DirectorySeparatorChar).Last();
var splitIdx = fileName.LastIndexOf(".");
var name = fileName.Substring(0, splitIdx);
var extension = fileName.Substring(splitIdx + 1);
if (name.IsEqualTo(templateName) && extension.IsEqualTo(_agentSettings.TemplateFormat))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. No guard for splitidx 📘 Rule violation ☼ Reliability

GetAgentTemplateDetail uses LastIndexOf(".") and then calls Substring without guarding for
-1, which can throw at the storage boundary when files don't match the expected naming format.
This violates the requirement to add explicit null/empty/invalid guards at provider boundaries and
return safe fallbacks.
Agent Prompt
## Issue description
Filename parsing assumes every file name contains a `.` and uses `Substring` with an unchecked index, which can throw and break reads at the repository boundary.

## Issue Context
This code runs against files in a directory and must tolerate unexpected/invalid file names.

## Fix Focus Areas
- src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs[746-750]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

{
var template = agent?.Templates?.FirstOrDefault(x => x.Name == options.TemplateName)?.Content ?? string.Empty;
instruction = BuildInstruction(template, options?.Data ?? []);
var template = agent?.Templates?.FirstOrDefault(x => x.Name == options.TemplateName);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Case-sensitive templatename match 📘 Rule violation ≡ Correctness

Template selection compares identifier-like names with ==, which is case-sensitive and can behave
inconsistently for identifier matching. This violates the requirement to use ordinal,
case-insensitive comparisons for identifiers.
Agent Prompt
## Issue description
Template lookup uses a case-sensitive equality operator for an identifier-like string (`TemplateName`), risking mismatches.

## Issue Context
Identifier comparisons must be culture-invariant and case-insensitive.

## Fix Focus Areas
- src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Instruct.cs[69-69]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines 574 to 576
public async Task<bool> PatchAgentTemplate(string agentId, AgentTemplate template)
{
if (string.IsNullOrEmpty(agentId) || template == null)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

3. Mongo patch drops config 🐞 Bug ≡ Correctness

MongoRepository.PatchAgentTemplate updates only Content, so template ResponseFormat/LlmConfig
changes sent via /agent/{agentId}/templates are silently lost when Mongo storage is used.
Agent Prompt
### Issue description
MongoRepository.PatchAgentTemplate persists only `Content`, dropping new per-template fields (`ResponseFormat`, `LlmConfig`) when templates are patched.

### Issue Context
`/agent/{agentId}/templates` uses `AgentTemplate` as request payload, so clients can submit `responseFormat`/`llmConfig` but Mongo storage will ignore them.

### Fix Focus Areas
- src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs[574-597]

### Implementation notes
Update the found template element to include:
- `foundTemplate.ResponseFormat = template.ResponseFormat;`
- `foundTemplate.LlmConfig = AgentTemplateLlmConfigMongoModel.ToMongoModel(template.LlmConfig);`

(or replace the element in the list with `AgentTemplateMongoElement.ToMongoElement(template)`), then persist the updated templates array.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown
Member

@yileicn yileicn Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

再检查一下 /review

MaxOutputTokens = agent.LlmConfig.MaxOutputTokens,
ReasoningEffortLevel = agent.LlmConfig.ReasoningEffortLevel
};
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里是否需要返回 ResponseFormat

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Response format is included in the template detail. It is outside the llm config

@yileicn
Copy link
Copy Markdown
Member

yileicn commented Apr 20, 2026

/review

@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented Apr 20, 2026

Persistent review updated to latest commit 5375da1

Comment on lines +245 to +261
var prompt = string.Empty;
var llmConfig = agent.LlmConfig;

if (!string.IsNullOrEmpty(templateName))
{
prompt = agentService.RenderTemplate(agent, templateName);
var templateLlmConfig = agent.Templates?.FirstOrDefault(x => x.Name.IsEqualTo(templateName))?.LlmConfig;
if (templateLlmConfig?.IsValid == true)
{
llmConfig = new AgentLlmConfig
{
Provider = templateLlmConfig.Provider,
Model = templateLlmConfig.Model,
MaxOutputTokens = templateLlmConfig.MaxOutputTokens,
ReasoningEffortLevel = templateLlmConfig.ReasoningEffortLevel
};
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Template override drops settings 🐞 Bug ≡ Correctness

When a template has a valid LlmConfig, InstructService replaces agent.LlmConfig with a new
AgentLlmConfig that only copies Provider/Model/MaxOutputTokens/ReasoningEffortLevel, resetting other
AgentLlmConfig fields to defaults for that run. This can change behavior (e.g., recursion depth or
modality configs) whenever template overrides are used.
Agent Prompt
### Issue description
When template LLM overrides apply, the code constructs a new `AgentLlmConfig` with only a subset of properties, which resets other agent-level settings (e.g. recursion depth and other modality configs).

### Issue Context
Template LLM config should override only the fields it intends to change while preserving the rest of the agent’s effective LLM configuration.

### Fix
- In both places where a template-level LLM config is applied, start from the existing agent config (clone/copy) and override only the specific fields from the template.
- Ensure fields like `IsInherit`, `MaxRecursionDepth`, and any nested configs remain unchanged unless explicitly overridden.

### Fix Focus Areas
- src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs[244-270]
- src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Instruct.cs[56-91]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants